import React, { useEffect, useState, useRef } from 'react'
import styles from './index.module.less'
import CAT from './img/cat.jpg'
import { Switch, Input, Radio, Select, InputNumber, Checkbox, Button, Table, message, DatePicker } from 'antd'
import { cloneDeep } from 'lodash'
import { throttle } from '../../utils/tool'
import classNames from 'classnames'

const { MonthPicker, RangePicker, WeekPicker, YearPicker } = DatePicker
const { Option } = Select
const CheckboxGroup = Checkbox.Group

const isEmptyObject = (obj) => {
	if (typeof obj !== 'object') return false
	const keys = Reflect.ownKeys(obj)
	return !keys.length
}

const MoveChild = (props) => {
	const {
		onDrop,
		setRevokeList,
		setCTop,
		setCLeft,
		dataList,
		setDataList,
		nodeSelect,
		curSelectNode,
		curSelectNodeIdx,
		setCurSelectNode,
		setCurSelectNodeIdx,
		setCurSelectDOM,
	} = props

	const containerRef = useRef()
	const leftLineRef = useRef()
	const topLineRef = useRef()

	const [isNodeMove, setIsNodeMove] = useState(false)

	const throttleTime = 16

	const [lineLeftNums, setLineLeftNums] = useState([])
	const [lineLeftMidNums, setLineLeftMidNums] = useState([])
	const [lineRightNums, setLineRightNums] = useState([])
	const [lineTopNums, setLineTopNums] = useState([])
	const [lineTopMidNums, setLineTopMidNums] = useState([])
	const [lineBtmNums, setLineBtmNums] = useState([])

	const [nodes, setNodes] = useState([])

	const [initNodeX, setInitNodeX] = useState(undefined)
	const [initNodeY, setInitNodeY] = useState(undefined)

	const [startX, setStartX] = useState(0)
	const [startY, setStartY] = useState(0)

	const [raf, setRaf] = useState(undefined)

	useEffect(() => {
		const top = containerRef.current.offsetTop
		const left = containerRef.current.offsetLeft
		setCTop(top)
		setCLeft(left)
	}, [])

	useEffect(() => {
		const nodes = Array.from(containerRef.current.childNodes).slice(2)
		setNodes(nodes)
		const newDataList = cloneDeep(dataList)
		if (!nodes.length) return
		nodes.forEach((item, idx) => {
			newDataList[idx].width = item.clientWidth + 2
			newDataList[idx].height = item.clientHeight + 2
		})

		setCurSelectNode(newDataList[curSelectNodeIdx])
		let leftNums = []
		let leftMidNums = []
		let rightNums = []
		let topNums = []
		let topMidNums = []
		let btmNums = []
		const flag = newDataList.every((item, idx) => {
			leftNums.push(item.left)
			leftMidNums.push(item.left + item.width / 2)
			rightNums.push(item.left + item.width)
			topNums.push(item.top)
			topMidNums.push(item.top + item.height / 2)
			btmNums.push(item.top + item.height)
			return item.height === dataList[idx].height && item.width === dataList[idx].width
		})
		setLineLeftNums(leftNums)
		setLineLeftMidNums(leftMidNums)
		setLineRightNums(rightNums)
		setLineTopNums(topNums)
		setLineTopMidNums(topMidNums)
		setLineBtmNums(btmNums)
		!flag && setDataList(newDataList)
	}, [dataList, curSelectNodeIdx])

	const getLineInfo = (tY, tX, node) => {
		const border = 1
		const nearDistance = 3
		// 当前选中节点的边信息
		const cTop = tY
		const cMidT = tY + node.clientHeight / 2
		const cBtm = tY + node.clientHeight
		const cLeft = tX
		const cMidW = tX + node.clientWidth / 2
		const cRight = tX + node.clientWidth

		// border处理 节点选中之后会有1px solid 的属性影响
		let topMidNums = lineTopMidNums.map((item) => item - border)
		let topNums = lineTopNums
		let btmNums = lineBtmNums.map((item) => item - 2 * border)
		let leftMidNums = lineLeftMidNums.map((item) => item - border)
		let leftNums = lineLeftNums
		let rightNums = lineRightNums.map((item) => item - 2 * border)
		// 过滤当前选中节点的线
		const filterLines = (lines, target) => {
			// 防止重复删除 使用深拷贝
			const newLines = cloneDeep(lines)
			const index = newLines.findIndex((item) => item === target)
			index >= 0 && newLines.splice(index, 1)
			return newLines
		}
		// 其他节点的边信息 去除了当前选中的节点边
		btmNums = filterLines(btmNums, initNodeY + node.clientHeight)
		topNums = filterLines(topNums, initNodeY)
		topMidNums = filterLines(topMidNums, initNodeY + node.clientHeight / 2)
		rightNums = filterLines(rightNums, initNodeX + node.clientWidth)
		leftNums = filterLines(leftNums, initNodeX)
		leftMidNums = filterLines(leftMidNums, initNodeX + node.clientWidth / 2)

		const tLines = [...topMidNums, ...topNums, ...btmNums]
		const lLines = [...leftMidNums, ...leftNums, ...rightNums]

		let lineTop = 0,
			lineLeft = 0,
			adjustY = 0,
			adjustX = 0,
			topFlag,
			leftFlag

		let tLineInfos = [],
			lLineInfos = [] // 所有符合相邻条件的边信息
		// 查询靠的近的边 设置需要移动的值
		tLines.forEach((item) => {
			;[cMidT, cTop, cBtm].forEach((i) => {
				if (tLineInfos.length) return
				if (Math.abs(i - item) <= nearDistance) {
					topFlag = true
					tLineInfos.push({ lineTop: item, adjustY: i - item })
				} else {
					topFlag = false
				}
			})
		})

		lLines.forEach((item) => {
			if (lLineInfos.length) return
			;[cMidW, cLeft, cRight].forEach((i) => {
				if (Math.abs(i - item) <= nearDistance) {
					leftFlag = true
					lLineInfos.push({ lineLeft: item, adjustX: i - item })
				} else {
					leftFlag = false
				}
			})
		})

		lineTop = tLineInfos[0] ? tLineInfos[0].lineTop : 0 // 优先级高的线的信息
		adjustY = tLineInfos[0] ? tLineInfos[0].adjustY : 0

		lineLeft = lLineInfos[0] ? lLineInfos[0].lineLeft : 0
		adjustX = lLineInfos[0] ? lLineInfos[0].adjustX : 0

		topFlag = lineTop !== 0
		leftFlag = lineLeft !== 0

		return { lineTop, lineLeft, topFlag, leftFlag, adjustY, adjustX }
	}

	const getNode = () => {
		return nodes[curSelectNodeIdx]
	}

	const onMouseDown = (event, target, idx) => {
		setIsNodeMove(true)
		setStartX(event.pageX)
		setStartY(event.pageY)
		setCurSelectNodeIdx(idx)

		setInitNodeX(target.left)
		setInitNodeY(target.top)
		setCurSelectNode({ ...target })

		setCurSelectDOM(nodes[idx])
	}

	const mouseMove = throttle((event) => {
		if (!isNodeMove) return
		if (curSelectNodeIdx === null) return
		const moveX = event.pageX - startX
		const moveY = event.pageY - startY
		const rAF_fn = () => {
			const node = getNode()
			if (node) {
				const tY = initNodeY + moveY
				const tX = initNodeX + moveX

				const { lineTop, lineLeft, topFlag, leftFlag, adjustY, adjustX } = getLineInfo(tY, tX, node)

				topLineRef.current.style.transform = `translateY(${lineTop}px)`
				leftLineRef.current.style.transform = `translateX(${lineLeft}px)`

				topLineRef.current.style.display = topFlag ? 'block' : 'none'
				leftLineRef.current.style.display = leftFlag ? 'block' : 'none'

				node.style.top = tY - adjustY + 'px'
				node.style.left = tX - adjustX + 'px'
			}
		}
		if (raf) {
			window.cancelAnimationFrame(raf)
		}
		const r = window.requestAnimationFrame(rAF_fn)
		setRaf(r)
	}, throttleTime)

	const mouseUp = () => {
		topLineRef.current.style.display = 'none'
		leftLineRef.current.style.display = 'none'
		if (isEmptyObject(curSelectNode)) return

		const node = getNode()
		if (curSelectNode) {
			curSelectNode.top = +node.style.top.replace('px', '')
			curSelectNode.left = +node.style.left.replace('px', '')
		}
		setRevokeList((l) => {
			l.push(cloneDeep(dataList))
			return [...l]
		})
		const newDataList = cloneDeep(dataList)
		newDataList[curSelectNodeIdx] = { ...curSelectNode }
		const setData = () => {
			setDataList(cloneDeep(newDataList))
		}
		setIsNodeMove(false)
		window.requestIdleCallback(setData, { timeout: 200 })
	}

	return (
		<div
			ref={containerRef}
			onDrop={onDrop}
			className={styles.container}
			onMouseMove={async (event) => {
				await event.persist()
				await mouseMove(event)
			}}
			onMouseUp={mouseUp}
			onClick={(event) => nodeSelect(event, 'container')}
		>
			<div className={styles.leftLine} ref={leftLineRef} />
			<div className={styles.topLine} ref={topLineRef} />
			{dataList.map((item, idx) => {
				return (
					<div
						onClick={(event) => nodeSelect(event, item.type)}
						onMouseDown={(event) => onMouseDown(event, item, idx)}
						key={item.uuid}
						className={classNames(styles.node, curSelectNodeIdx === idx && styles.curSelectNode)}
						style={{ top: item.top, left: item.left, zIndex: item.zIndex }}
					>
						{item.type === 'text' && (
							<div
								className={styles.disable}
								style={{
									fontSize: item.fontSize,
									color: item.color,
								}}
							>
								{item.value}
							</div>
						)}
						{item.type === 'number' && <InputNumber type="number" className={styles.disable} />}
						{item.type === 'button' && (
							<Button
								className={styles.noEditBtn}
								type={item.btnType || 'default'}
								ghost={item.btnType !== 'default' && item.simple}
								size={item.size}
								style={{ fontSize: item.fontSize || 14 }}
							>
								{item.value}
							</Button>
						)}
						{item.type === 'switch' && (
							<Switch
								defaultChecked
								checkedChildren={item.onText}
								unCheckedChildren={item.offText}
								className={styles.disable}
							/>
						)}
						{item.type === 'image' && <img src={CAT} alt="" className={styles.disable} />}
						{item.type === 'textInput' && <Input className={styles.disable} />}
						{item.type === 'radio' && (
							<Radio.Group value={1} className={styles.disable}>
								{item.options &&
									item.options.length > 0 &&
									item.options.map((i, t) => (
										<Radio value={i.value} key={t}>
											{i.label}
										</Radio>
									))}
							</Radio.Group>
						)}
						{item.type === 'checkbox' && (
							<CheckboxGroup className={styles.disable} value={item.value}>
								{item.options &&
									item.options.length > 0 &&
									item.options.map((i, t) => (
										<Checkbox key={t} value={i.value}>
											{i.label}
										</Checkbox>
									))}
							</CheckboxGroup>
						)}
						{item.type === 'select' && (
							<Select className={styles.disable} value={item.options[0].value}>
								{item.options &&
									item.options.length > 0 &&
									item.options.map((i, t) => (
										<Option key={t} value={i.value}>
											{i.label}
										</Option>
									))}
							</Select>
						)}
						{/*日期选择器*/}
						{item.type === 'datepicker' && item.pickerType === 'default' && (
							<DatePicker className={styles.disable} value={item.value} />
						)}
						{item.type === 'datepicker' && item.pickerType === 'year' && (
							<YearPicker className={styles.disable} value={item.value} />
						)}
						{item.type === 'datepicker' && item.pickerType === 'month' && (
							<MonthPicker className={styles.disable} value={item.value} />
						)}
						{item.type === 'datepicker' && item.pickerType === 'week' && (
							<WeekPicker className={styles.disable} value={item.value} />
						)}
						{item.type === 'datepicker' && item.pickerType === 'range' && (
							<RangePicker
								className={styles.disable}
								value={[
									item.startField ? item[item.startField] : item.startTime,
									item.endField ? item[item.endField] : item.endTime,
								]}
							/>
						)}
					</div>
				)
			})}
		</div>
	)
}

export default MoveChild
